
`include "common_header.verilog"

//  *************************************************************************
//  File : pause_rxdp_64
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2014 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Thomas Schrobenhauser
//  info@morethanip.com
//  *************************************************************************
//  Description : Flow Control (Pause/PFC) Receive Datapath
//  Version     : $Id: pause_rxdp_64.v,v 1.4 2019/03/05 19:28:06 dk Exp $
//  *************************************************************************

module pause_rxdp_64 (
   reset_rxclk,
   rx_clk,
   rx_data_ff,
   rx_sop_ff,
   rx_eop_ff,
   rx_mod_ff,
   rx_wren_ff,
   rx_dval_ff,
   rx_afull_ff,
   rx_empty_st_ff,
   rx_preamble_ff,
   rx_stat_wren_ff,
   rx_stat_data_ff,
   rx_data_mac,
   rx_sop_mac,
   rx_eop_mac,
   rx_mod_mac,
   rx_wren_mac,
   rx_dval_mac,
   rx_afull_mac,
   rx_empty_st_mac,
   rx_preamble_mac,
   rx_stat_wren_mac,
   rx_stat_data_mac,
   pause_quant_val,
   pause_quant_avb,
   mac_addr,
   pause_fwd,
   rx_pause_bypass,
   pause_ignore,
   cmd_frm_ena,
   pfc_mode
   );

parameter       RX_STAT_DAT_WIDTH_64 = 25;

input   reset_rxclk;                            //  Active High reset for rx_clk domain
input   rx_clk;                                 //  XLGMII Receive clock
output  [63:0] rx_data_ff;                      //  Receive Data to FIFO
output  rx_sop_ff;                              //  Receive Start of Packet
output  rx_eop_ff;                              //  Receive End of Packet
output  [2:0] rx_mod_ff;                        //  Receive last word modulo
output  rx_wren_ff;                             //  Receive Data FIFO write enable
output  rx_dval_ff;                             //  Receive Data valid
input   rx_afull_ff;                            //  Receive Data FIFO almost full
input   rx_empty_st_ff;                         //  RX FIFO empty (data+status)
output  [55:0] rx_preamble_ff;                  //  Receive frame preamble (stable from sop)
output  rx_stat_wren_ff;                        //  Receive Status FIFO write enable
output  [RX_STAT_DAT_WIDTH_64-1:0] rx_stat_data_ff;                 //  Receive Frame Status & Error indications
input   [63:0] rx_data_mac;                     //  Receive Data to FIFO
input   rx_sop_mac;                             //  Receive Start of Packet
input   rx_eop_mac;                             //  Receive End of Packet
input   [2:0] rx_mod_mac;                       //  Receive last word modulo
input   rx_wren_mac;                            //  Receive Data FIFO write enable
input   rx_dval_mac;                            //  Receive Data valid
output  rx_afull_mac;                           //  Receive Data FIFO almost full
output  rx_empty_st_mac;                        //  RX FIFO empty (data+status)
input   [55:0] rx_preamble_mac;                 //  Receive frame preamble (stable from sop)
input   rx_stat_wren_mac;                       //  Receive Status FIFO write enable
input   [RX_STAT_DAT_WIDTH_64-1:0] rx_stat_data_mac;                //  Receive Frame Status & Error indications

output  [135:0] pause_quant_val;                //  Pause Quanta value from rx path: enable(8)+8*quanta(16)
output  pause_quant_avb;                        //  Pause Quanta value available from rx path
input   [47:0] mac_addr;                        //  Device Ethernet MAC address
input   pause_fwd;                              //  Forward Pause Frames to Application
input   rx_pause_bypass;                        //  if 1 the pause frame removal datapath function is bypassed
input   pause_ignore;                           //  Ignore Pause Frames Quantas
input   cmd_frm_ena;                            //  Enable Command Frames
input   pfc_mode;                               //  Priority flow control enabled

wire     [63:0] rx_data_ff;
wire     rx_sop_ff;
wire     rx_eop_ff;
wire     [2:0] rx_mod_ff;
wire    rx_wren_ff;
wire    rx_dval_ff;
wire     [55:0] rx_preamble_ff;
wire    rx_stat_wren_ff;
wire     [RX_STAT_DAT_WIDTH_64-1:0] rx_stat_data_ff;

reg     [63:0]  rx_data_ff_int;
reg             rx_sop_ff_int;
reg             rx_eop_ff_int;
reg     [2:0]   rx_mod_ff_int;
wire            rx_wren_ff_int;
reg     [55:0]  rx_preamble_ff_int;
wire            rx_stat_wren_ff_int;
reg     [RX_STAT_DAT_WIDTH_64-1:0] rx_stat_data_ff_int;


wire    rx_afull_mac;
wire    rx_empty_st_mac;
wire    [135:0] pause_quant_val;
reg     pause_quant_avb;

reg     rx_wren_int;
reg     rx_stat_wren_int;
reg     pword1;
reg     pword2;
reg     [3:0] frm_cnt;
wire    frame_disc;
reg     frame_disc_r;
wire    [15:0] type_len_field;          //  Type/Length field of incoming frame
wire    [15:0] opcode_field;            //  Opcode field of incoming frame
wire    is_mac_addr;                    //  Destination address matches mac_addr
wire    is_ctrl;                        //  Control frame (pause) address detected
wire    frame_pause_type;               //  Frame pause type
wire    frame_pause_opcode;             //  Frame pause opcode
wire    pause_frame_good;               //  Pause frame is good
reg     addr_uni_match_d;               //  Destination address unicast match, latched
reg     addr_pause_ok_d;                //  Destination address pause ok, latched
reg     pause_frame_declared;           //  Pause frame is identified
wire    rxclk_ena;


// Datapath register
// -----------------
always @(posedge rx_clk or posedge reset_rxclk)
   begin
   if (reset_rxclk == 1'b 1)
      begin
      rx_data_ff_int       <= {64{1'b 0}};
      rx_sop_ff_int        <= 1'b 0;
      rx_eop_ff_int        <= 1'b 0;
      rx_mod_ff_int        <= {3{1'b 0}};
      rx_wren_int          <= 1'b 0;
      rx_preamble_ff_int   <= {56{1'b 0}};
      rx_stat_wren_int     <= 1'b 0;
      rx_stat_data_ff_int  <= {RX_STAT_DAT_WIDTH_64{1'b 0}};

      end
   else
      begin
      // dval
      if (rx_dval_mac == 1'b 1)
         begin
         rx_data_ff_int       <= rx_data_mac;
         rx_sop_ff_int        <= rx_sop_mac;
         rx_eop_ff_int        <= rx_eop_mac;
         rx_mod_ff_int        <= rx_mod_mac;
         rx_wren_int          <= rx_wren_mac;
         rx_preamble_ff_int   <= rx_preamble_mac;
         rx_stat_wren_int     <= rx_stat_wren_mac;
         rx_stat_data_ff_int  <= rx_stat_data_mac;
         end
      end
   end

// write enables gated with dval (clock enable)
assign rx_wren_ff_int      = rx_wren_int      & rx_dval_mac & ~frame_disc & ~frame_disc_r;
assign rx_stat_wren_ff_int = rx_stat_wren_int & rx_dval_mac & ~frame_disc & ~frame_disc_r;

// dval ("true" clock enable)
assign rx_dval_ff = rx_dval_mac;

//  Bypass mode support


assign rx_data_ff               = (rx_pause_bypass == 1'b 0)? rx_data_ff_int       : rx_data_mac;
assign rx_sop_ff                = (rx_pause_bypass == 1'b 0)? rx_sop_ff_int        : rx_sop_mac;
assign rx_eop_ff                = (rx_pause_bypass == 1'b 0)? rx_eop_ff_int        : rx_eop_mac;
assign rx_mod_ff                = (rx_pause_bypass == 1'b 0)? rx_mod_ff_int        : rx_mod_mac;
assign rx_preamble_ff           = (rx_pause_bypass == 1'b 0)? rx_preamble_ff_int   : rx_preamble_mac;
assign rx_stat_data_ff          = (rx_pause_bypass == 1'b 0)? rx_stat_data_ff_int  : rx_stat_data_mac;
assign rx_wren_ff               = (rx_pause_bypass == 1'b 0)? rx_wren_ff_int       : rx_wren_mac;
assign rx_stat_wren_ff          = (rx_pause_bypass == 1'b 0)? rx_stat_wren_ff_int  : rx_stat_wren_mac;




assign rx_afull_mac    = rx_afull_ff;
assign rx_empty_st_mac = rx_empty_st_ff;


// Frame word counter/word indication
// ----------------------------------
always @(posedge rx_clk or posedge reset_rxclk)
   begin
   if (reset_rxclk == 1'b 1)
      begin
      pword1 <= 1'b 0;
      pword2 <= 1'b 0;
      frm_cnt <= 4'b 0000;
      end
   else
      begin
      // dval
      if (rx_dval_mac == 1'b 1) // not masked with rx_wren_mac, as pword1 must deassert on 2nd dval.
         begin
         // pause frame word 1 (Type/Length field / Opcode field)
         pword1 <= rx_sop_mac;

         // start word indication for quanta extraction
         pword2 <= pword1 & frame_pause_type;

         // frame word counter
         if (rx_sop_mac == 1'b 1)
            begin
            frm_cnt <= 4'b 0001;
            end
         else if (frm_cnt < 4'b 1000)
            begin
            frm_cnt <= frm_cnt + 4'b 0001;
            end
         end
      end
   end


// Frame discarding
// ----------------
assign frame_disc = pword1 == 1'b 1 & frame_pause_type == 1'b 1 &
                    (frame_pause_opcode == 1'b 1 & pause_fwd == 1'b 0 |
                     frame_pause_opcode == 1'b 0 & cmd_frm_ena == 1'b 0) ? 1'b 1 : 1'b 0;

always @(posedge rx_clk or posedge reset_rxclk)
   begin
   if (reset_rxclk == 1'b 1)
      begin
      frame_disc_r <= 1'b 0;
      end
   else
      begin
      // dval
      if (rx_dval_mac == 1'b 1 & rx_wren_mac == 1'b 1)
         begin
         if (frame_disc == 1'b 1)
            begin
            frame_disc_r <= 1'b 1;
            end
         else if (rx_sop_mac == 1'b 1)   // clear at next sop
            begin
            frame_disc_r <= 1'b 0;
            end
         end
      end
   end


// Field extraction
// ----------------
// Type/Length field / Opcode field of incoming frame
assign type_len_field = {rx_data_mac[39:32], rx_data_mac[47:40]};
assign opcode_field   = {rx_data_mac[55:48], rx_data_mac[63:56]};

// Destination address matches mac_addr
assign is_mac_addr = rx_data_mac[47:0] == mac_addr ? 1'b 1 : 1'b 0;

// Control frame (pause frame) detected
assign is_ctrl = rx_data_mac[47:0] == 48'h 010000c28001 ? 1'b 1 : 1'b 0;

assign frame_pause_type   = type_len_field == 16'h 8808 ? 1'b 1 : 1'b 0;
assign frame_pause_opcode = (pfc_mode == 1'b 0 & opcode_field == 16'h 0001 |
                             pfc_mode == 1'b 1 & opcode_field == 16'h 0101) ? 1'b 1 : 1'b 0;

// Pause frame is good (CRC okay)
assign pause_frame_good = pause_frame_declared == 1'b 1 & rx_eop_mac == 1'b 1 & rx_stat_data_mac[1] == 1'b 0 &
                          frm_cnt == 4'b 0111 & rx_mod_mac == 3'b 000 ? 1'b 1 : 1'b 0;

// Destination address checks latched
always @(posedge rx_clk or posedge reset_rxclk)
   begin
   if (reset_rxclk == 1'b 1)
      begin
      addr_uni_match_d <= 1'b 0;
      addr_pause_ok_d <= 1'b 0;
      pause_frame_declared <= 1'b 0;
      end
   else
      begin
      // dval
      if (rx_dval_mac == 1'b 1 & rx_wren_mac == 1'b 1)
         begin
         if (rx_sop_mac == 1'b 1)
            begin
            addr_uni_match_d <= is_mac_addr;
            addr_pause_ok_d <= is_ctrl;
            end

         if (pword1 == 1'b 1 & (addr_uni_match_d == 1'b 1 | addr_pause_ok_d == 1'b 1) &
             frame_pause_type == 1'b 1 & frame_pause_opcode == 1'b 1)
            begin
            pause_frame_declared <= 1'b 1;
            end
         else if (rx_sop_mac == 1'b 1)
            begin
            pause_frame_declared <= 1'b 0;
            end
         end
      end
   end


// Pause quanta value available
// ----------------------------
always @(posedge rx_clk or posedge reset_rxclk)
   begin
   if (reset_rxclk == 1'b 1)
      begin
      pause_quant_avb <= 1'b 0;
      end
   else
      begin
      // dval
      if (rx_dval_mac == 1'b 1)
         begin
         if (rx_wren_mac == 1'b 1 & pause_frame_good == 1'b 1 & pause_ignore == 1'b 0)
            begin
            pause_quant_avb <= 1'b 1;
            end
         else
            begin
            pause_quant_avb <= 1'b 0;
            end
         end
      end
   end


// Collect pause quanta(s)
// -----------------------
assign rxclk_ena = rx_dval_mac & rx_wren_mac;

pause_qextract_64 U_PQX (
          .reset_rxclk(reset_rxclk),
          .xgmii_rxclk(rx_clk),
          .xgmii_rxclk_ena(rxclk_ena),
          .pfc_mode(pfc_mode),
          .rx_d(rx_data_mac),
          .psop(pword2),
          .pause_quant_val(pause_quant_val)
          );


endmodule // module pause_rxdp_64
